package org.chs.ts.batch.threadservice;

import lombok.extern.slf4j.Slf4j;


import org.chs.ts.base.batch.log.entity.BatchClearBasicLog;
import org.chs.ts.base.batch.vo.entity.BatchExecResp;
import org.chs.ts.base.common.util.oConvertUtils;
import org.chs.ts.base.constant.CommonConstant;
import org.chs.ts.base.vo.Result;
import org.chs.ts.batch.util.CommonUtil;
import org.chs.ts.batch.controller.BatchExecController;
import org.chs.ts.batch.service.IBatchClearBasicLogService;

import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Compay 盛唐天行
 * @Author: wuwei
 * @Censor By wuwei
 * @Sence 2023/12/18 10:31
 * @Modify 2023/12/18 10:31
 * @Description
 */
@Service
@Scope("prototype")
@Slf4j
public class BatchExecAsyncService {


    @Resource
    private BatchExecController batchExecController;

    @Resource
    private Environment environment;

    @Resource
    private IBatchClearBasicLogService clearBasicLogService;

    private final AtomicLong MAX_ERROR_COUNT = new AtomicLong(5);
    private final AtomicLong MAX_WAITTING_TIME = new AtomicLong(5000);

    private void createClearBasicLog(boolean runningResult, String errMsg, String currentDate, String logSernoPrdfix) {
        if (logSernoPrdfix.length() > 19) {
            logSernoPrdfix = logSernoPrdfix.substring(0, 19);
            log.error("createClearBasicLog -> logSernoPrdfix参数过长请及时调整，参数长度应小于等于1位： " + logSernoPrdfix + "[" + logSernoPrdfix.length() + "]");
        }
        currentDate = currentDate.isEmpty() ? new SimpleDateFormat("yyyyMMdd").format(new Date()) : currentDate;
        String ip = CommonUtil.getLocalHostAddress();
        String port = environment.getProperty("server.port");
        String path = oConvertUtils.getString(environment.getProperty("server.servlet.context-path"));

        BatchClearBasicLog clearBasicLog = new BatchClearBasicLog();
        clearBasicLog.setSysOrgCode("");//所属部门
        clearBasicLog.setLogSerno(logSernoPrdfix.toUpperCase(Locale.ROOT) + System.currentTimeMillis());//日志号
        clearBasicLog.setTaskDate(currentDate);//清算业务日期
        clearBasicLog.setExecStatus(runningResult ? "3" : "4");//执行状态 (0-未执行; 1-占用中; 2-执行中;   3-执行成功; 4-执行失败; 5-跳过执行; 6-任务终止; )
        clearBasicLog.setExecDate(currentDate);//执行日期
        clearBasicLog.setShouldExecDate(currentDate);//应执行日期
        clearBasicLog.setServerName(path);//服务器主机名
        clearBasicLog.setServerIp(ip + ":" + port);//服务器IP
        clearBasicLog.setRtnCode(runningResult ? CommonConstant.RTN_CODE_SUCCESS : CommonConstant.RTN_CODE_FAILED);//返回码
        clearBasicLog.setRtnDesc(errMsg);//执行结束信息

        clearBasicLogService.save(clearBasicLog);

    }

    private void createClearBasicLog(boolean runningResult, String errMsg, String currentDate) {
        String logSernoPrdfix = "BatchTaskScheduled";
        createClearBasicLog(runningResult, errMsg, currentDate, logSernoPrdfix);
    }


    /**
     * 开启 跑批状态 如开启失败 将进行多次开启
     *
     * @param taskGroup 跑批过程组
     * @param execDate  跑批执行时间
     * @return 开启指定跑批执行时间 跑批过程组 状态
     */
    private boolean startTaskRunningStatus(String batchSysCode, String taskGroup, String execDate, String snatchingMode) {
        Boolean taskRunningStatus = batchExecController.getTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
        if (!taskRunningStatus) {
            for (int i = 0; i < MAX_ERROR_COUNT.get(); i++) {
                log.info("BatchTaskScheduled 执行日志-第[" + (i + 1) + "]次开启跑批状态 taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                taskRunningStatus = batchExecController.startTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
                // 开启成功 终止当前循环
                if (taskRunningStatus) {
                    break;
                }
                // 开启失败 暂停5000毫秒
                try {
                    Thread.sleep(MAX_WAITTING_TIME.get());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return taskRunningStatus;
    }


    private boolean stopTaskRunningStatus(String batchSysCode, String taskGroup, String execDate, String snatchingMode) {
        Boolean taskRunningStatus = batchExecController.getTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
        boolean resultStatus = false;
        if (taskRunningStatus) {
            for (int i = 0; i < MAX_ERROR_COUNT.get(); i++) {
                log.info("BatchTaskScheduled 执行日志-第[" + (i + 1) + "]次开启跑批状态 batchSysCode -> [" + batchSysCode + "] taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                resultStatus = batchExecController.stopTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
                // 开启成功 终止当前循环
                if (resultStatus) {
                    break;
                }
                // 开启失败 暂停5000毫秒
                try {
                    Thread.sleep(MAX_WAITTING_TIME.get());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        } else {
            // 当前 运行状态 为未运行 无需进行关闭操作 故返回结果为true
            resultStatus = true;
        }
        return resultStatus;

    }


    @Async("threadPoolBatchTaskExecutor")
    public void asyncDoJob(String batchSysCode, String taskGroup, String execDate, String snatchingMode) {

        log.info("BatchTaskScheduled 执行日志-获取可执行文件组 batchSysCode -> [" + batchSysCode + "] taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        createClearBasicLog(true, "BatchTaskScheduled 执行日志-获取可执行文件组 batchSysCode -> [" + batchSysCode + "] taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), "");
        boolean runningStatus = startTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
        if (!runningStatus) {
            createClearBasicLog(false, "BatchTaskScheduled 执行日志-开启跑批状态失败 taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), "");
            return;
        }
        int i = 0;
        while (i < MAX_ERROR_COUNT.get()) {
            //log.info("BatchTaskScheduled 执行日志:跑批定时任务!获取跑批任务 taskGroup[" + taskGroup + "] execDate[" + execDate + "] snatchingMode[" + snatchingMode + "] " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            Result<BatchExecResp> task = batchExecController.getTask(batchSysCode, taskGroup, execDate, snatchingMode);
            if (task.getResult().getRtnCode().equals("000000")) {
                // 获取 待执行记录 进行执行
                //log.info("BatchTaskScheduled 执行日志:跑批定时任务!执行跑批任务 taskGroup[" + taskGroup + "] execDate[" + execDate + "] snatchingMode[" + snatchingMode + "] taskExecId[" + task.getResult().getTaskExecId() + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                try {
                    batchExecController.process(task.getResult().getTaskExecId(), snatchingMode);
                    createClearBasicLog(false, "BatchTaskScheduled 执行日志:跑批定时任务!任务编号" + task.getResult().getTaskExecId() + "运行" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), execDate);
                } catch (Exception e) {
                    // e.printStackTrace();
                    createClearBasicLog(false, "BatchTaskScheduled 执行日志:跑批定时任务!任务编号" + task.getResult().getTaskExecId() + "异常" + e.getMessage() + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), execDate);
                }
                try {
                    Thread.sleep(MAX_WAITTING_TIME.get());
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                }
                // 计数器清零
                i = 0;
            }

            if (task.getResult().getRtnCode().equals("999999")) {
                //log.info("BatchTaskScheduled 执行日志:跑批定时任务!第" + (i + 1) + "次抢夺跑批任务失败 taskGroup[" + taskGroup + "] execDate[" + execDate + "] snatchingMode[" + snatchingMode + "] taskExecId[" + task.getResult().getTaskExecId() + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                // 未获取 待执行任务 进行幂等 稍后调用
                try {
                    Thread.sleep((long) Math.pow(2, (double) i++ / 5) * MAX_WAITTING_TIME.get());
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                }
                continue;
            }

            if (task.getResult().getRtnCode().equals("111111")) {
                //log.info("BatchTaskScheduled 执行日志:跑批定时任务!执行跑批任务完成 taskGroup[" + taskGroup + "] execDate[" + execDate + "] snatchingMode[" + snatchingMode + "] taskExecId[" + task.getResult().getTaskExecId() + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                // 关闭 跑批任务
                boolean stopRunningStatus = stopTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);
                if (!stopRunningStatus) {
                    createClearBasicLog(false, "BatchTaskScheduled 执行日志-关闭跑批状态失败 batchSysCode -> [" + batchSysCode + "] taskGroup -> [" + taskGroup + "] execDate -> [" + execDate + "] snatchingMode -> [" + snatchingMode + "]" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), "");
                    return;
                }

                // 执行列表结束 跳出循环
                break;
            }
        }

        // 针对达到 运行最大次数 而未完成的内容 关闭运行状态
        stopTaskRunningStatus(batchSysCode, taskGroup, execDate, snatchingMode);

        log.info("-------------------------------" + CommonUtil.getLocalHostAddress() + " -> BatchTaskScheduled 执行结束" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

    }
}

